//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         Xort compiler execution subsystem
// FILE:            xniobject.cpp - runtime user declared object in third party dll
// AUTHOR:          Vladimir Gumenuk
// DATE:            21-Aug-2014
// MODIFICATIONS:
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "xort.h"
#include "xobject.h"
#include "xniobject.h"
#include "xruntime.h"
#include "xcode.h"
#include "XNIVariantArray.h"

#ifdef BUILD_FOR_PC
    #include <windows.h>
#elif defined( BUILD_FOR_MAC )
    #include <dlfcn.h>
#endif


XNIObject::XNIObject( XRuntime *pRuntime ) : XObject( pRuntime )
{
    m_pNativeObj = NULL;    
}

XNIObject::~XNIObject()
{
}

// builds the object fields
int XNIObject::Init( symbolref pSymbol, XortNativeInterface *pExternalObj )
{
    if( !( pSymbol->m_dwFlags & f_import_obj ) )
        return ERR_INTERNAL;

    int iRet = XObject::Init( pSymbol, NULL );
    if( iRet != XORT_OK )
        return iRet;

    string csObjName = pSymbol->FindAttribute( attr_import );
    if( csObjName.Compare( "object" ) != 0 )
        return ERR_INVALID_ATTRIBUTE;

    csObjName = pSymbol->FindAttribute( attr_name );
    if( csObjName.IsEmpty() )
        return ERR_INVALID_ATTRIBUTE;
    
    if( pExternalObj ) // external object created elsewhere...
    {
        m_pNativeObj = pExternalObj;
        m_pNativeObj->AddRef();
    }
    else // if object is not passed as argument, we should create it
    {
        typedef int (__stdcall create_xort_object_t)(lpcstr lpszName, xunknown **ppOut);
        create_xort_object_t *pfnCreateXortObject = NULL;

        string csModule  = pSymbol->FindAttribute( attr_module ); 
        if( csModule.IsEmpty() )
            return ERR_INVALID_ATTRIBUTE;

#ifdef BUILD_FOR_PC
        HINSTANCE hDLL = GetModuleHandle( csModule.GetBuffer() );
        if( !hDLL )
        {
            hDLL = LoadLibrary( csModule.GetBuffer() );
            if( !hDLL )
            {
                char szPath[MAX_PATH];
                GetModuleFileNameA( NULL, szPath, MAX_PATH );

                strcat( szPath, "\\" );
                strcat( szPath, csModule.GetBuffer() );

                hDLL = LoadLibrary( szPath );
                if( !hDLL )
                    return ERR_LOAD_LIBRARY_FAIL;
            }
        }
    
        pfnCreateXortObject = (create_xort_object_t *)GetProcAddress( hDLL, "create_xort_object" );
        if( !pfnCreateXortObject )
            return ERR_GET_PROC_ADDR_FAIL;

#elif defined( BUILD_FOR_MAC )
    #error Not implemented yet!
#else
    #error Unsupported platform!
#endif

        iRet = pfnCreateXortObject( csObjName.GetBuffer(), (xunknown**)&m_pNativeObj );
        if( iRet != XORT_OK )
            return iRet;
    }

    // verify that we got object with the same name:
    if( csObjName.Compare( m_pNativeObj->GetName() ) != 0 ) {
        Uninit();
        return ERR_INVALID_OBJECT_NAME;
    }

    return iRet;
}

// cleans the object fields
void XNIObject::Uninit()
{
    if( m_pNativeObj ) {
        m_pNativeObj->Release();
        m_pNativeObj = NULL;
    }

    XObject::Uninit();
}

// Invokes the object method
int XNIObject::Invoke( dword dwFuncIndex, XRuntime *pRuntime )
{
    int iRet = XORT_OK;

    symbolref pMethod = get_Method( dwFuncIndex );
    if( !pMethod )
        return ERR_METHOD_NOT_FOUND;

    InstructionData retPrim; retPrim.m_bInstrCode = p_ret; retPrim.m_bTypeAddr = t_v; // prepare ret primitive
    XNIVariant vResult;

    XCELLSARRAY& stack = pRuntime->get_Stack();
    const XortElement& sysFrame = stack.top();
    dword dwStackBase = pRuntime->get_StackBase();
    dword dwArgsCount = (dword)sysFrame.bType;

    if( sysFrame.bAccess != sys_Frame || pMethod->m_dwArgumentsCount != dwArgsCount )
        return ERR_INVALID_STACK_SYSFRAME;
    
    if( dwArgsCount == 0 ) {
        // invoke native method    
        iRet = m_pNativeObj->Invoke( pMethod->m_csName.GetBuffer(), NULL, vResult );

    } else {

        CXNIVariantArrayImpl *pArgsArray = NULL;
        iRet = CXNIVariantArrayImpl::Create( &pArgsArray );
        if( iRet != XORT_OK )
            return iRet;

        // get direct pointer to the first argument
        const XortElement *pStkArg = (XortElement *)stack.ptr( dwStackBase - dwArgsCount );

        // get symbol that described this argument
        SYMBOL_LIST::iterator theIter = pMethod->m_listMembers.end();
        theIter--; // skip local scope marker
        symbolref pSymArg = (theIter == NULL) ? NULL : (Symbol*)theIter;

        while( dwArgsCount-- )
        {
            XNIVariant vArg;

            SymbolRef SymType = FindType( pRuntime->get_GlobalSymbolList(), pSymArg->m_dwType );

            iRet = pStkArg->CopyToXNIVariant( vArg, &SymType );
            if( iRet != XORT_OK )
                return iRet;

            pArgsArray->AddVariant( vArg );

            theIter--;
            pSymArg = (theIter == NULL) ? NULL : (Symbol*)theIter;
            pStkArg++;
        }

        // invoke native method with some arguments
        iRet = m_pNativeObj->Invoke( pMethod->m_csName.GetBuffer(), pArgsArray, vResult );

        pArgsArray->Release();
    }

    if( iRet != XORT_OK ) // error is fatal in this case
        return iRet;

    pRuntime->do_ret( retPrim );

    if( pMethod->m_dwType != t_v ) 
    {
        XortElement xElem;
        
        SymbolRef retType = FindType( pRuntime->get_GlobalSymbolList(), pMethod->m_dwType );

        iRet = xElem.InitFromXNIVariant( vResult, &retType, pRuntime );
        if( iRet != XORT_OK )
            return iRet;

        stack.push( xElem );
    }

    return iRet;
}

// assigns new value to the internal variable
int XNIObject::Put( dword dwVarIndex, const XortElement& Value )
{
    int iRet = ERR_METHOD_NOT_IMPL;
    return iRet;
}

// returns the value of the internal variable
int XNIObject::Get( dword dwVarIndex, XortElement& Value )
{
    int iRet = ERR_METHOD_NOT_IMPL;
    return iRet;
}

